
// INCLUDES ********************************************************************

#include "FileUtils.h"

#include "Configuration.h"

#include <CoreFoundation/CoreFoundation.h>

#include <Foundation/Foundation.h>

#include <sys/stat.h>

#include <unistd.h>

#include <sys/types.h>

#include <fstream>

#include <stdexcept>

// NAMESPACES ******************************************************************

namespace
{
    const uint32_t gMaxPathLength = 1024;
}

// *****************************************************************************

const std::string Meteor::FileUtils::ResourcePath()
{
    CFBundleRef mainBundle = CFBundleGetMainBundle();
    
    CFURLRef bundleURL = CFBundleCopyBundleURL( mainBundle);

    char urlBuffer[ gMaxPathLength];
    
    bool isValid = CFURLGetFileSystemRepresentation( bundleURL, 
                                                     true, 
                                                     ( uint8_t*) urlBuffer, 
                                                     gMaxPathLength);
    
    CFRelease( bundleURL);
    
    if ( isValid == true)
    {
        std::string path ( urlBuffer);
    
        path.append("/");
    
        return path;
    }
    
    return std::string( "");
}

// -----------------------------------------------------------------------------

const std::string Meteor::FileUtils::ResourcePathForFile( const std::string& fileName)
{
    std::string BundlePath = ResourcePath();
    
    std::string filePath = BundlePath.append( fileName);
    
    if ( Configuration::Instance().ValueForKey<bool>( "Retina support"))
    {  
        std::string retinaFile = filePath.insert( filePath.find_last_of("."), "-hd");
        
        if ( ResourceExists( retinaFile))
        {
            return retinaFile;
        }
    }
    
    return filePath;
}

// -----------------------------------------------------------------------------

const std::string Meteor::FileUtils::WritableDirectorY()
{
    NSArray* directories = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, 
                                                                NSUserDomainMask,
                                                                YES);
                                                                 
    NSString* documentsPath = [directories objectAtIndex:0];
    
    const char* pathNoSlash = [documentsPath UTF8String];
    
    std::string path( pathNoSlash);
    
    path.append("/");
    
    return path;
}

// -----------------------------------------------------------------------------

const std::string Meteor::FileUtils::WritablePathForFile( const std::string& fileName)
{
    std::string WritablePath = WritableDirectorY();
    
    std::string filePath = WritablePath.append( fileName);

    return filePath;
    
    return std::string( "");
}


// -----------------------------------------------------------------------------

bool Meteor::FileUtils::ResourceExists( std::string filename)
{
    std::string filePath = ResourcePath();
    
    filePath.append( filename);
    
    struct stat fileInfo;
    
    /// @brief stat returns zero if successful
    if ( stat( filePath.c_str(), &fileInfo))
    {
        return false;
    }
    else
    {
        return true;
    }
}

// -----------------------------------------------------------------------------

const bool Meteor::FileUtils::WriteFile( const std::string& data,
                                         const std::string& fileName, 
                                         const std::string& subDirectory)
{
    std::ofstream file;
    
    std::string filePath = WritableDirectorY();
    
    filePath.append( fileName);
    
    file.open( filePath.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
   
    if ( !file.is_open())
    {
        Logger::DebugLog( "FileUtils::WriteFile - failed to open file.");
        
        return false;
    }
    
    file << data;
    
    file.close();
    
    return true;
}

// -----------------------------------------------------------------------------

const bool Meteor::FileUtils::ReadFile( const std::string& fileName,
                                        char* buffer,
                                        const size_t numBytes,
                                        const size_t offset,
                                        const std::string& subDirectory)
{
    try
    {
        if ( buffer == nullptr)
        {
            throw std::invalid_argument( "FileUtils::ReadFile - invalid buffer provided.");
        }
        
        std::string filePath = WritablePathForFile( fileName);
        
        std::ifstream file;
        
        file.open( filePath.c_str(), std::ios::binary);
        
        if ( file.is_open() != true)
        {
            throw std::invalid_argument( "FileUtils::ReadFile - failed to open file.");
        }
        
        file.seekg( offset, std::ios::beg);

        if ( file.eof() == true)
        {
            file.close();
        
            throw std::invalid_argument( "FileUtils::ReadFile - invalid offset provided.");
        }

        file.read( buffer, numBytes);
        
        file.close();
        
        return true;
    }
    catch ( const std::exception& exc)
    {
        Logger::DebugLog( exc.what());
        
        return false;
    }
}

// -----------------------------------------------------------------------------

const bool Meteor::FileUtils::ReadFile( const std::string& fileName,
                                        char* buffer,
                                        const std::string& subDirectory)
{
    struct stat fileInfo;
    
    stat( WritablePathForFile( fileName).c_str(), &fileInfo);

    return ReadFile( fileName, buffer, static_cast<size_t>( fileInfo.st_size));
}
